feat(wallet): give new full-node wallets a BIP39 seed phrase + backup UI
New full-node wallets now get a real 24-word recovery phrase, and users can reveal/back it up from Settings. - Pass -usemnemonic=1 to dragonxd. The daemon reads it only inside GenerateNewSeed() when a wallet has no seed yet, so it is inert on existing wallets (safe to pass unconditionally) and makes every freshly-created wallet mnemonic-backed — its phrase is then exportable via z_exportmnemonic and portable to SDXLite/ObsidianDragonLite. (Existing/legacy wallets are unaffected and keep using the chat identity's z_exportkey fallback.) - App::exportSeedPhrase() wraps z_exportmnemonic on the RPC worker with sodium_memzero wiping on every path; it flags the daemon's "not derived from a mnemonic" error as a legacy wallet rather than a failure. - New "Back up seed phrase" modal (Settings -> Backup & Data, full-node gated): reveals the phrase in a numbered word grid, copy (45s clipboard auto-clear) + save-to-file (0600), wipes the secret on every close path incl. dismiss-mid-fetch. Shared ui/windows/seed_display.h grid helper. - One-time nudge (settings flag seed_backup_reminded) toasts mnemonic-wallet users to back up their phrase; legacy wallets are not nagged. - 15 new strings translated into all 8 languages (additive, 957->972 keys); CJK subset font rebuilt for the new glyphs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
46
src/ui/windows/seed_display.h
Normal file
46
src/ui/windows/seed_display.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
// Shared helpers for rendering a BIP39 seed phrase as a numbered word grid and for
|
||||
// splitting a whitespace-separated phrase into words. Used by the full-node seed-phrase
|
||||
// backup dialog and the "migrate to a seed wallet" flow so both present the phrase the
|
||||
// same way the lite welcome wizard does.
|
||||
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "imgui.h"
|
||||
|
||||
namespace dragonx {
|
||||
namespace ui {
|
||||
|
||||
// Split a whitespace-separated seed phrase into its individual words.
|
||||
inline std::vector<std::string> SplitSeedWords(const std::string& phrase)
|
||||
{
|
||||
std::vector<std::string> words;
|
||||
std::string cur;
|
||||
for (char c : phrase) {
|
||||
if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
|
||||
if (!cur.empty()) { words.push_back(cur); cur.clear(); }
|
||||
} else {
|
||||
cur.push_back(c);
|
||||
}
|
||||
}
|
||||
if (!cur.empty()) words.push_back(cur);
|
||||
return words;
|
||||
}
|
||||
|
||||
// Render the words as a numbered 4-column grid (matches the lite welcome wizard layout).
|
||||
inline void RenderSeedWordGrid(const std::vector<std::string>& words, float colSpacingPx = 130.0f)
|
||||
{
|
||||
for (size_t i = 0; i < words.size(); ++i) {
|
||||
char cell[96];
|
||||
std::snprintf(cell, sizeof(cell), "%2zu. %s", i + 1, words[i].c_str());
|
||||
ImGui::TextUnformatted(cell);
|
||||
if ((i % 4) != 3 && i + 1 < words.size())
|
||||
ImGui::SameLine(((i % 4) + 1) * colSpacingPx);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ui
|
||||
} // namespace dragonx
|
||||
Reference in New Issue
Block a user