Compare commits
2
Commits
3f1838cfdd
...
a30a083df0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a30a083df0 | ||
|
|
89d3975eb3 |
+94
-34
@@ -4762,6 +4762,9 @@ void App::renderSeedMigrationDialog()
|
||||
if (!seed_migration_seed_.empty())
|
||||
sodium_memzero(&seed_migration_seed_[0], seed_migration_seed_.size());
|
||||
seed_migration_seed_.clear();
|
||||
// Disarm the inline confirms so an armed state can never survive a close/reopen.
|
||||
seed_migration_confirm_discard_ = false;
|
||||
seed_migration_confirm_sweep_ = false;
|
||||
};
|
||||
auto close = [this, &wipeSeed]() { wipeSeed(); show_seed_migration_ = false; };
|
||||
|
||||
@@ -4895,11 +4898,15 @@ void App::renderSeedMigrationDialog()
|
||||
}
|
||||
ImGui::Spacing();
|
||||
ImGui::Checkbox(TR("mig_backed_up"), &seed_migration_backed_up_);
|
||||
ImGui::Checkbox(TR("mig_understood"), &seed_migration_understood_);
|
||||
ImGui::Spacing();
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ui::material::OnSurfaceMedium());
|
||||
ImGui::TextWrapped("%s", TR("mig_step1_done"));
|
||||
ImGui::PopStyleColor();
|
||||
if (!seed_migration_backed_up_) ImGui::BeginDisabled();
|
||||
// Continue requires BOTH acknowledgements, and is suppressed while the Discard confirm is armed.
|
||||
const bool ackOk = seed_migration_backed_up_ && seed_migration_understood_;
|
||||
const bool continueDisabled = !ackOk || seed_migration_confirm_discard_;
|
||||
if (continueDisabled) ImGui::BeginDisabled();
|
||||
if (ui::material::TactileButton(TR("mig_continue_sweep"), ImVec2(170 * dp, 0))) {
|
||||
wipeSeed(); // the sweep/adopt steps only use the address, never the seed itself
|
||||
seed_migration_step_ = SeedMigrationStep::Sweep;
|
||||
@@ -4907,23 +4914,40 @@ void App::renderSeedMigrationDialog()
|
||||
seed_migration_nofunds_confirmed_ = false;
|
||||
refreshSeedMigrationBalance();
|
||||
}
|
||||
if (!seed_migration_backed_up_) ImGui::EndDisabled();
|
||||
if (continueDisabled) ImGui::EndDisabled();
|
||||
if (!ackOk && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled))
|
||||
ui::material::Tooltip("%s", TR("mig_ack_required"));
|
||||
ImGui::SameLine();
|
||||
if (ui::material::TactileButton(TR("mig_discard"), ImVec2(160 * dp, 0))) {
|
||||
// Throw away the isolated new wallet + clear the pending state.
|
||||
if (!seed_migration_temp_dir_.empty()) {
|
||||
std::error_code ec;
|
||||
std::filesystem::remove_all(seed_migration_temp_dir_, ec);
|
||||
// Discard now takes a second click: first click arms a confirm, since it deletes the new wallet.
|
||||
if (!seed_migration_confirm_discard_) {
|
||||
if (ui::material::TactileButton(TR("mig_discard"), ImVec2(160 * dp, 0)))
|
||||
seed_migration_confirm_discard_ = true;
|
||||
}
|
||||
if (seed_migration_confirm_discard_) {
|
||||
ImGui::Spacing();
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(ui::material::ReadableError()));
|
||||
ImGui::TextWrapped("%s", TR("mig_discard_confirm"));
|
||||
ImGui::PopStyleColor();
|
||||
if (ui::material::TactileButton(TR("mig_discard_yes"), ImVec2(160 * dp, 0))) {
|
||||
// Throw away the isolated new wallet + clear the pending state. (body unchanged)
|
||||
if (!seed_migration_temp_dir_.empty()) {
|
||||
std::error_code ec;
|
||||
std::filesystem::remove_all(seed_migration_temp_dir_, ec);
|
||||
}
|
||||
if (settings_) {
|
||||
settings_->setSeedMigrationPending(false);
|
||||
settings_->setSeedMigrationDest("");
|
||||
settings_->setSeedMigrationTempDir("");
|
||||
settings_->save();
|
||||
}
|
||||
seed_migration_temp_dir_.clear();
|
||||
seed_migration_dest_.clear();
|
||||
seed_migration_confirm_discard_ = false;
|
||||
close();
|
||||
}
|
||||
if (settings_) {
|
||||
settings_->setSeedMigrationPending(false);
|
||||
settings_->setSeedMigrationDest("");
|
||||
settings_->setSeedMigrationTempDir("");
|
||||
settings_->save();
|
||||
}
|
||||
seed_migration_temp_dir_.clear();
|
||||
seed_migration_dest_.clear();
|
||||
close();
|
||||
ImGui::SameLine();
|
||||
if (ui::material::TactileButton(TR("cancel"), ImVec2(110 * dp, 0)))
|
||||
seed_migration_confirm_discard_ = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -4999,17 +5023,35 @@ void App::renderSeedMigrationDialog()
|
||||
ImGui::Spacing();
|
||||
{
|
||||
const bool canSweep = !state_.isLocked() && seed_migration_balance_ > 0.0;
|
||||
if (!canSweep) ImGui::BeginDisabled();
|
||||
if (ui::material::TactileButton(TR("mig_sweep_all"), ImVec2(170 * dp, 0)))
|
||||
beginSweepToSeedWallet();
|
||||
if (!canSweep) ImGui::EndDisabled();
|
||||
if (!canSweep) seed_migration_confirm_sweep_ = false; // never keep a stale armed state
|
||||
if (!seed_migration_confirm_sweep_) {
|
||||
if (!canSweep) ImGui::BeginDisabled();
|
||||
if (ui::material::TactileButton(TR("mig_sweep_all"), ImVec2(170 * dp, 0)))
|
||||
seed_migration_confirm_sweep_ = true; // first click arms the confirm
|
||||
if (!canSweep) ImGui::EndDisabled();
|
||||
ImGui::SameLine();
|
||||
if (ui::material::TactileButton(TR("refresh"), ImVec2(110 * dp, 0)))
|
||||
refreshSeedMigrationBalance();
|
||||
ImGui::SameLine();
|
||||
if (ui::material::TactileButton(TR("mig_later"), ImVec2(100 * dp, 0)))
|
||||
close(); // pending state persists; resumes here next time
|
||||
} else {
|
||||
// Armed: state the amount being moved before the single sweep transaction fires.
|
||||
char cbuf[160]; snprintf(cbuf, sizeof(cbuf), TR("mig_sweep_confirm"), seed_migration_balance_);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(ui::material::ReadableError()));
|
||||
ImGui::TextWrapped("%s", cbuf);
|
||||
ImGui::PopStyleColor();
|
||||
if (!canSweep) ImGui::BeginDisabled();
|
||||
if (ui::material::TactileButton(TR("mig_sweep_confirm_yes"), ImVec2(170 * dp, 0))) {
|
||||
seed_migration_confirm_sweep_ = false;
|
||||
beginSweepToSeedWallet(); // unchanged
|
||||
}
|
||||
if (!canSweep) ImGui::EndDisabled();
|
||||
ImGui::SameLine();
|
||||
if (ui::material::TactileButton(TR("cancel"), ImVec2(110 * dp, 0)))
|
||||
seed_migration_confirm_sweep_ = false;
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ui::material::TactileButton(TR("refresh"), ImVec2(110 * dp, 0)))
|
||||
refreshSeedMigrationBalance();
|
||||
ImGui::SameLine();
|
||||
if (ui::material::TactileButton(TR("mig_later"), ImVec2(100 * dp, 0)))
|
||||
close(); // pending state persists; resumes here next time
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -5049,17 +5091,35 @@ void App::renderSeedMigrationDialog()
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ui::material::ReadableError());
|
||||
ImGui::TextWrapped("%s", TR("mig_remainder"));
|
||||
ImGui::PopStyleColor();
|
||||
if (ui::material::TactileButton(TR("mig_sweep_remaining"), ImVec2(180 * dp, 0)))
|
||||
beginSweepToSeedWallet();
|
||||
if (!seed_migration_confirm_sweep_) {
|
||||
if (ui::material::TactileButton(TR("mig_sweep_remaining"), ImVec2(180 * dp, 0)))
|
||||
seed_migration_confirm_sweep_ = true; // first click arms
|
||||
} else {
|
||||
char rcbuf[160];
|
||||
snprintf(rcbuf, sizeof(rcbuf), TR("mig_sweep_confirm"), seed_migration_legacy_remaining_);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(ui::material::ReadableError()));
|
||||
ImGui::TextWrapped("%s", rcbuf);
|
||||
ImGui::PopStyleColor();
|
||||
if (ui::material::TactileButton(TR("mig_sweep_confirm_yes"), ImVec2(180 * dp, 0))) {
|
||||
seed_migration_confirm_sweep_ = false;
|
||||
beginSweepToSeedWallet(); // unchanged
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ui::material::TactileButton(TR("cancel"), ImVec2(110 * dp, 0)))
|
||||
seed_migration_confirm_sweep_ = false;
|
||||
}
|
||||
} else {
|
||||
ImGui::TextColored(kMedium, "%s", TR("mig_waiting_mine"));
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ui::material::TactileButton(TR("refresh"), ImVec2(110 * dp, 0)))
|
||||
pollSweepStatus(/*forceBalance=*/true);
|
||||
ImGui::SameLine();
|
||||
if (ui::material::TactileButton(TR("mig_later"), ImVec2(100 * dp, 0)))
|
||||
close(); // pending state + txid persist; resumes here next time
|
||||
// Refresh / Later are hidden while the sweep confirm is armed, to keep that row uncluttered.
|
||||
if (!seed_migration_confirm_sweep_) {
|
||||
ImGui::SameLine();
|
||||
if (ui::material::TactileButton(TR("refresh"), ImVec2(110 * dp, 0)))
|
||||
pollSweepStatus(/*forceBalance=*/true);
|
||||
ImGui::SameLine();
|
||||
if (ui::material::TactileButton(TR("mig_later"), ImVec2(100 * dp, 0)))
|
||||
close(); // pending state + txid persist; resumes here next time
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -927,6 +927,8 @@ private:
|
||||
// curve on every failed path (a PIN RPC failure used to skip it).
|
||||
void applyUnlockSuccess(const std::string& passphrase, int timeout);
|
||||
void applyUnlockFailure(const std::string& errorMessage);
|
||||
// Restore the persisted unlock-failure count + lockout (once per lock-screen session, per wallet).
|
||||
void restoreLockoutState();
|
||||
// Non-credential failure (busy node, unknown error): message only, no attempt counted.
|
||||
void applyUnlockNotCounted(const std::string& errorMessage);
|
||||
// Clear all rescan + witness-rebuild progress/accumulator state. Shared by the four
|
||||
@@ -1453,6 +1455,9 @@ private:
|
||||
std::string seed_migration_dest_; // new shielded z-address (Phase 2 sweep target)
|
||||
std::string seed_migration_temp_dir_; // temp datadir root holding the new wallet (kept)
|
||||
bool seed_migration_backed_up_ = false; // "I've written it down" confirmation
|
||||
bool seed_migration_understood_ = false; // 2nd ack: funds recoverable only via the phrase
|
||||
bool seed_migration_confirm_discard_ = false; // inline "are you sure" gate for Discard (never persisted)
|
||||
bool seed_migration_confirm_sweep_ = false; // inline "are you sure" gate for Sweep (never persisted)
|
||||
std::string seed_migration_status_; // main-thread display (progress / error text)
|
||||
double seed_migration_balance_ = 0.0; // legacy total to sweep (shown on the Sweep step)
|
||||
std::string seed_migration_sweep_txid_; // the z_mergetoaddress sweep transaction id
|
||||
@@ -1973,6 +1978,7 @@ private:
|
||||
int lock_attempts_ = 0;
|
||||
float lock_lockout_timer_ = 0.0f;
|
||||
std::chrono::steady_clock::time_point lock_timer_armed_at_{}; // when lock_error/lockout timers were last armed
|
||||
bool lock_state_restored_ = false; // one-shot: persisted lockout restored for this lock-screen session
|
||||
bool lock_unlock_in_progress_ = false;
|
||||
|
||||
// Encrypt wallet dialog state
|
||||
|
||||
@@ -1673,6 +1673,7 @@ void App::switchToWallet(const std::string& walletFile, bool stopDaemonConfirmed
|
||||
pin_vault_stale_ = false; // the out-of-date mark was the previous wallet's vault
|
||||
lock_attempts_ = 0;
|
||||
lock_lockout_timer_ = 0.0f;
|
||||
lock_state_restored_ = false; // re-restore persisted lockout for the newly-active wallet
|
||||
lock_error_msg_.clear();
|
||||
lock_error_timer_ = 0.0f;
|
||||
lock_unlock_in_progress_ = false;
|
||||
@@ -7031,6 +7032,7 @@ void App::pumpSeedMigration()
|
||||
seed_migration_dest_ = std::move(dest);
|
||||
seed_migration_temp_dir_ = std::move(tmp);
|
||||
seed_migration_backed_up_ = false;
|
||||
seed_migration_understood_ = false;
|
||||
seed_migration_step_ = SeedMigrationStep::ShowSeed;
|
||||
// Persist the pending migration so a later sweep/adopt step can find the new wallet.
|
||||
if (settings_) {
|
||||
|
||||
+46
-4
@@ -33,6 +33,7 @@
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
@@ -476,6 +477,12 @@ void App::applyUnlockSuccess(const std::string& passphrase, int timeout) {
|
||||
lock_unlock_in_progress_ = false;
|
||||
lock_error_msg_.clear();
|
||||
lock_attempts_ = 0;
|
||||
// Clear the persisted lockout on a successful unlock (guarded so a normal unlock isn't a disk write).
|
||||
if (settings_ && settings_->getLockFailedAttempts() != 0) {
|
||||
settings_->setLockFailedAttempts(0);
|
||||
settings_->setLockoutUntilEpoch(0);
|
||||
settings_->save();
|
||||
}
|
||||
memset(lock_passphrase_buf_, 0, sizeof(lock_passphrase_buf_));
|
||||
idle_.noteActivity();
|
||||
state_.encrypted = true;
|
||||
@@ -499,9 +506,37 @@ void App::applyUnlockFailure(const std::string& errorMessage) {
|
||||
if (lock_attempts_ >= maxAttempts) {
|
||||
lock_lockout_timer_ = baseDelay * (float)(1 << std::min(lock_attempts_ - maxAttempts, 8));
|
||||
}
|
||||
// Persist the attempt count + lockout expiry (keyed to this wallet) so killing and relaunching the
|
||||
// app can't reset the escalating lockout. The PIN/passphrase itself is never stored.
|
||||
if (settings_) {
|
||||
settings_->setLockStateWallet(settings_->getActiveWalletFile());
|
||||
settings_->setLockFailedAttempts(lock_attempts_);
|
||||
settings_->setLockoutUntilEpoch(lock_lockout_timer_ > 0.0f
|
||||
? (long long)std::time(nullptr) + (long long)std::ceil(lock_lockout_timer_) : 0);
|
||||
settings_->save();
|
||||
}
|
||||
DEBUG_LOGF("[App] Wallet unlock failed (attempt %d)\n", lock_attempts_);
|
||||
}
|
||||
|
||||
// Restore the persisted lockout at the start of a lock-screen session so a kill-and-relaunch can't reset
|
||||
// the escalating failure curve. Only applies when the persisted state is for the active wallet.
|
||||
void App::restoreLockoutState() {
|
||||
if (!settings_) return;
|
||||
if (settings_->getLockStateWallet() != settings_->getActiveWalletFile()) return;
|
||||
lock_attempts_ = std::max(0, std::min(settings_->getLockFailedAttempts(), 64));
|
||||
long long remaining = settings_->getLockoutUntilEpoch() - (long long)std::time(nullptr);
|
||||
// Cap the restored countdown so a forward clock change can't create an absurd wait. The attempt count
|
||||
// still persists, so rolling the clock back only buys a short window before the next failure re-arms
|
||||
// the full curve.
|
||||
const float baseDelay = ui::schema::UI().drawElement("security", "lockout-base-delay").sizeOr(2.0f);
|
||||
const long long maxLock = (long long)(baseDelay * (float)(1 << 8));
|
||||
if (remaining > maxLock) remaining = maxLock;
|
||||
if (remaining > 0) {
|
||||
lock_lockout_timer_ = (float)remaining;
|
||||
lock_timer_armed_at_ = std::chrono::steady_clock::now();
|
||||
}
|
||||
}
|
||||
|
||||
// Failure that is NOT a wrong credential (busy node, not-encrypted, unknown error): surface the
|
||||
// message and clear the buffer, but do not bump lock_attempts_ or arm the lockout.
|
||||
void App::applyUnlockNotCounted(const std::string& msg) {
|
||||
@@ -1040,9 +1075,15 @@ void App::tickMiningThreadCommit()
|
||||
void App::renderLockScreen() {
|
||||
using namespace ui::material;
|
||||
|
||||
// Restore any persisted lockout once per lock-screen session (re-armed on wallet switch).
|
||||
if (!lock_state_restored_) {
|
||||
lock_state_restored_ = true;
|
||||
restoreLockoutState();
|
||||
}
|
||||
|
||||
// Keep frames coming while something on the lock screen is moving without input: the unlock in
|
||||
// flight (its dots), the lockout countdown, an error message about to clear.
|
||||
if (lock_unlock_in_progress_ || lock_lockout_timer_ > 0.0f || lock_error_timer_ > 0.0f) {
|
||||
if (lock_unlock_in_progress_ || lock_lockout_timer_ > 0.0f) {
|
||||
RequestProgressFrame();
|
||||
}
|
||||
|
||||
@@ -1264,9 +1305,9 @@ void App::renderLockScreen() {
|
||||
lock_screen_was_visible_ = true;
|
||||
}
|
||||
|
||||
// Error message
|
||||
if (!lock_error_msg_.empty() && lock_error_timer_ > 0) {
|
||||
lock_error_timer_ -= lockDt;
|
||||
// Error message — stays visible until the next unlock attempt / mode switch / success, instead of
|
||||
// fading after a few seconds (a failed-unlock reason was often gone before it could be read).
|
||||
if (!lock_error_msg_.empty()) {
|
||||
ImVec2 es = captionFont->CalcTextSizeA(captionFont->LegacySize, FLT_MAX, 0, lock_error_msg_.c_str());
|
||||
dl->AddText(captionFont, captionFont->LegacySize,
|
||||
ImVec2(cardX + (cardW - es.x) * 0.5f, cy), ui::material::Error(),
|
||||
@@ -1312,6 +1353,7 @@ void App::renderLockScreen() {
|
||||
|
||||
// Handle submit (Enter key or button click)
|
||||
if ((submitted || btnClicked) && canSubmit) {
|
||||
lock_error_msg_.clear(); // the prior error clears only when a new attempt actually starts
|
||||
// Derive daemon unlock timeout from auto-lock setting:
|
||||
// 2x the idle timeout so the daemon stays unlocked longer than
|
||||
// the GUI auto-lock, acting as a safety net if the GUI crashes.
|
||||
|
||||
+4
-1
@@ -354,6 +354,9 @@ void App::clearDemoWalletData()
|
||||
seed_migration_precheck_ = SeedMigrationPrecheck::Pending;
|
||||
seed_migration_precheck_started_ = false;
|
||||
seed_migration_backed_up_ = false;
|
||||
seed_migration_understood_ = false;
|
||||
seed_migration_confirm_discard_ = false;
|
||||
seed_migration_confirm_sweep_ = false;
|
||||
seed_migration_balance_ = 0.0;
|
||||
seed_migration_balance_loaded_ = false;
|
||||
seed_migration_nofunds_confirmed_ = false;
|
||||
@@ -623,7 +626,7 @@ void App::buildSweepCatalog()
|
||||
a.seed_migration_precheck_started_ = true;
|
||||
});
|
||||
mig("modal-migrate-showseed", SeedMigrationStep::ShowSeed,
|
||||
[](App& a) { a.seed_migration_seed_ = kDemoMnemonic; a.seed_migration_backed_up_ = false; });
|
||||
[](App& a) { a.seed_migration_seed_ = kDemoMnemonic; a.seed_migration_backed_up_ = false; a.seed_migration_understood_ = false; });
|
||||
mig("modal-migrate-sweep", SeedMigrationStep::Sweep,
|
||||
[](App& a) { a.seed_migration_balance_ = 15.75000000; a.seed_migration_balance_loaded_ = true; });
|
||||
mig("modal-migrate-nofunds", SeedMigrationStep::Sweep,
|
||||
|
||||
@@ -271,6 +271,9 @@ bool Settings::load(const std::string& path)
|
||||
}
|
||||
loadScalar(j, "wizard_completed", wizard_completed_);
|
||||
loadScalar(j, "seed_backup_reminded", seed_backup_reminded_);
|
||||
loadScalar(j, "lock_failed_attempts", lock_failed_attempts_);
|
||||
loadScalar(j, "lockout_until_epoch", lockout_until_epoch_);
|
||||
loadScalar(j, "lock_state_wallet", lock_state_wallet_);
|
||||
loadScalar(j, "large_wallet_warned", large_wallet_warned_);
|
||||
if (j.contains("empty_wallet_warning_acked") && j["empty_wallet_warning_acked"].is_array()) {
|
||||
empty_wallet_warning_acked_.clear();
|
||||
@@ -596,6 +599,9 @@ bool Settings::save(const std::string& path)
|
||||
}
|
||||
j["wizard_completed"] = wizard_completed_;
|
||||
j["seed_backup_reminded"] = seed_backup_reminded_;
|
||||
j["lock_failed_attempts"] = lock_failed_attempts_;
|
||||
j["lockout_until_epoch"] = lockout_until_epoch_;
|
||||
j["lock_state_wallet"] = lock_state_wallet_;
|
||||
j["large_wallet_warned"] = large_wallet_warned_;
|
||||
j["empty_wallet_warning_acked"] = json::array();
|
||||
for (const auto& w : empty_wallet_warning_acked_)
|
||||
|
||||
@@ -393,6 +393,15 @@ public:
|
||||
bool getSeedBackupReminded() const { return seed_backup_reminded_; }
|
||||
void setSeedBackupReminded(bool v) { seed_backup_reminded_ = v; }
|
||||
|
||||
// Unlock lockout state, persisted across restarts so the failed-attempt curve can't be reset by
|
||||
// killing the app. Keyed by the wallet file it applies to; the PIN/passphrase is never stored.
|
||||
int getLockFailedAttempts() const { return lock_failed_attempts_; }
|
||||
void setLockFailedAttempts(int v) { lock_failed_attempts_ = v; durable_next_save_ = true; }
|
||||
long long getLockoutUntilEpoch() const { return lockout_until_epoch_; }
|
||||
void setLockoutUntilEpoch(long long v) { lockout_until_epoch_ = v; durable_next_save_ = true; }
|
||||
std::string getLockStateWallet() const { return lock_state_wallet_; }
|
||||
void setLockStateWallet(const std::string& v) { lock_state_wallet_ = v; durable_next_save_ = true; }
|
||||
|
||||
// One-time nudge when wallet.dat grows past the bloat threshold (re-armed if it shrinks back).
|
||||
bool getLargeWalletWarned() const { return large_wallet_warned_; }
|
||||
void setLargeWalletWarned(bool v) { large_wallet_warned_ = v; }
|
||||
@@ -736,6 +745,9 @@ private:
|
||||
std::map<std::string, AddressMeta> address_meta_;
|
||||
bool wizard_completed_ = false;
|
||||
bool seed_backup_reminded_ = false;
|
||||
int lock_failed_attempts_ = 0; // persisted unlock-failure count (lockout curve)
|
||||
long long lockout_until_epoch_ = 0; // unix time the current lockout expires (0 = none)
|
||||
std::string lock_state_wallet_; // wallet file the above two apply to
|
||||
bool large_wallet_warned_ = false;
|
||||
std::set<std::string> empty_wallet_warning_acked_; // wallet files whose empty-wallet warning was dismissed
|
||||
bool encryption_pending_ = false;
|
||||
|
||||
@@ -474,6 +474,8 @@ void I18n::loadBuiltinEnglish()
|
||||
strings_["mig_receive_addr"] = "New wallet receive address:";
|
||||
strings_["mig_copy_seed"] = "Copy seed";
|
||||
strings_["mig_backed_up"] = "I've written down my seed phrase";
|
||||
strings_["mig_understood"] = "I understand that once I continue, my funds will be moved into this new wallet and can only be recovered with this seed phrase";
|
||||
strings_["mig_ack_required"] = "Confirm both statements above to continue";
|
||||
strings_["mig_step1_done"] = "Step 1 of 2 complete — your funds have NOT moved yet. Sweeping them into this new wallet is the next step.";
|
||||
strings_["mig_continue_sweep"] = "Continue to sweep";
|
||||
strings_["mig_discard"] = "Discard migration";
|
||||
@@ -491,6 +493,10 @@ void I18n::loadBuiltinEnglish()
|
||||
strings_["mig_adopt"] = "Make this my wallet";
|
||||
strings_["mig_remainder"] = "Some funds are still in your old wallet (too many inputs for one transaction) — sweep the remainder before switching.";
|
||||
strings_["mig_sweep_remaining"] = "Sweep remaining";
|
||||
strings_["mig_discard_confirm"] = "Discard this migration? The new seed wallet created on this device will be deleted. Make sure you have not sent anything to it. Your current wallet is not changed.";
|
||||
strings_["mig_discard_yes"] = "Yes, discard";
|
||||
strings_["mig_sweep_confirm"] = "Send %.8f DRGX from your current wallet to the new seed wallet in a single transaction? This moves all of your funds.";
|
||||
strings_["mig_sweep_confirm_yes"] = "Yes, sweep funds";
|
||||
strings_["mig_waiting_mine"] = "Waiting for the sweep transaction to be mined…";
|
||||
strings_["mig_adopting"] = "Installing your new wallet and rescanning…";
|
||||
strings_["mig_done"] = "Migration complete. Your wallet is now backed by your seed phrase.";
|
||||
|
||||
Reference in New Issue
Block a user