// DragonX Wallet - ImGui Edition // Copyright 2024-2026 The Hush Developers // Released under the GPLv3 #include "seed_phrase.h" #include namespace dragonx { namespace util { std::string normalizeSeedPhrase(const std::string& raw) { // Unicode whitespace encoded as UTF-8, each mapped to a single ASCII space; and zero-width marks // to strip. We substitute only these EXACT byte sequences, so ordinary (ASCII) word bytes are // never touched — the common all-ASCII phrase just gets its spacing collapsed and trimmed. static const char* const kSpaces[] = { "\xC2\xA0", // U+00A0 NBSP "\xC2\x85", // U+0085 NEL "\xE1\x9A\x80", // U+1680 ogham space "\xE2\x80\x80", "\xE2\x80\x81", "\xE2\x80\x82", "\xE2\x80\x83", // U+2000–2003 "\xE2\x80\x84", "\xE2\x80\x85", "\xE2\x80\x86", "\xE2\x80\x87", // U+2004–2007 "\xE2\x80\x88", "\xE2\x80\x89", "\xE2\x80\x8A", // U+2008–200A "\xE2\x80\xAF", // U+202F narrow NBSP "\xE2\x81\x9F", // U+205F math space "\xE3\x80\x80", // U+3000 ideographic }; static const char* const kZeroWidth[] = { "\xE2\x80\x8B", "\xE2\x80\x8C", "\xE2\x80\x8D", // U+200B/C/D "\xEF\xBB\xBF", // U+FEFF BOM / ZWNBSP }; std::string s = raw; auto replaceAll = [&s](const std::string& from, const std::string& to) { if (from.empty()) return; std::size_t pos = 0; while ((pos = s.find(from, pos)) != std::string::npos) { s.replace(pos, from.size(), to); pos += to.size(); } }; for (const char* zw : kZeroWidth) replaceAll(zw, ""); for (const char* sp : kSpaces) replaceAll(sp, " "); // Collapse ASCII whitespace runs to a single space and trim ends. std::string out; out.reserve(s.size()); bool pendingSpace = false; bool sawWord = false; for (unsigned char c : s) { if (std::isspace(c)) { pendingSpace = sawWord; continue; } if (pendingSpace) { out.push_back(' '); pendingSpace = false; } out.push_back(static_cast(c)); sawWord = true; } return out; } int seedPhraseWordCount(const std::string& phrase) { int words = 0; bool inWord = false; for (unsigned char c : phrase) { const bool space = std::isspace(c) != 0; if (space) inWord = false; else if (!inWord) { inWord = true; ++words; } } return words; } bool isCompleteRecoveryPhrase(int words) { return words == 24; } } // namespace util } // namespace dragonx