From 493124b485bfa44bacbcadfd6469bb2683465a2c Mon Sep 17 00:00:00 2001 From: Konstantin Ullrich Date: Tue, 25 Feb 2025 13:22:02 +0100 Subject: [PATCH] feat: Improve compatibility for japanese strings --- CHANGELOG.md | 6 +++++- lib/src/mnemonics/polyseed_lang.dart | 12 +++++++++--- lib/src/polyseed.dart | 17 +++++++++++------ pubspec.yaml | 2 +- test/polyseed_test.dart | 19 +++++++++++++++++++ 5 files changed, 45 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 925c12a..bb4bdce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,8 @@ -## 0.0.5 +## 0.0.7 + +- Improve compatibility with japanese strings using U+0020 separators + +## 0.0.6 - Make getters less strict - Support up-to-date dependencies diff --git a/lib/src/mnemonics/polyseed_lang.dart b/lib/src/mnemonics/polyseed_lang.dart index 92491d7..a9751c1 100644 --- a/lib/src/mnemonics/polyseed_lang.dart +++ b/lib/src/mnemonics/polyseed_lang.dart @@ -60,13 +60,15 @@ class PolyseedLang { /// Get the [PolyseedLang] by it's english name eg. "Chinese (Simplified)" static PolyseedLang getByEnglishName(String englishName) => - languages.firstWhere((e) => e.nameEnglish.toLowerCase() == englishName.toLowerCase()); + languages.firstWhere( + (e) => e.nameEnglish.toLowerCase() == englishName.toLowerCase()); /// Get the [PolyseedLang] using the words of [phrase] static PolyseedLang getByPhrase(String phrase) { for (var language in languages) { phrase = language.hasAccents ? unorm.nfkd(phrase) : phrase; - final phraseWords = phrase.split(language.separator); + final phraseWords = + language.normalizeSeparator(phrase).split(language.separator); if (language.words.containsAll(phraseWords)) { return language; } @@ -85,7 +87,7 @@ class PolyseedLang { } /// Decode a valid seed [phrase] into it's coefficients - List decodePhrase(String phrase) => phrase + List decodePhrase(String phrase) => normalizeSeparator(phrase) .split(separator) .map((e) => words.indexOf(hasAccents ? unorm.nfkd(e) : e)) .toList(); @@ -93,4 +95,8 @@ class PolyseedLang { /// Encode a seed [coefficients] into a valid seed phrase String encodePhrase(List coefficients) => coefficients.map((e) => words[e]).join(separator); + + /// Replace space with the expected [separator] to improve compatibility + String normalizeSeparator(String phrase) => + separator != '\u0020' ? phrase.replaceAll("\u0020", separator) : phrase; } diff --git a/lib/src/polyseed.dart b/lib/src/polyseed.dart index 9a09690..bb6c5f3 100644 --- a/lib/src/polyseed.dart +++ b/lib/src/polyseed.dart @@ -21,11 +21,15 @@ class Polyseed { late PolyseedData _data; /// Check if a seed is a valid Polyseed - static bool isValidSeed(String phrase) { - if (!PolyseedLang.isValidPhrase(phrase)) return false; - final lang = PolyseedLang.getByPhrase(phrase); - - return phrase.split(lang.separator).length == numberOfWords; + static bool isValidSeed(String phrase, [PolyseedLang? lang]) { + if (lang != null && !PolyseedLang.isValidPhrase(phrase)) return false; + final polyseedLang = lang ?? PolyseedLang.getByPhrase(phrase); + + return polyseedLang + .normalizeSeparator(phrase) + .split(polyseedLang.separator) + .length == + numberOfWords; } /// Create a random [Polyseed] @@ -66,9 +70,10 @@ class Polyseed { Polyseed.decode(String str, PolyseedLang lang, PolyseedCoin coin) { assert(coin.index < GFPoly.size); - final words = str.split(lang.separator); + final words = lang.normalizeSeparator(str).split(lang.separator); final poly = GFPoly(); + // split into words if (words.length != numberOfWords) { throw WordNumberException(); diff --git a/pubspec.yaml b/pubspec.yaml index 03d6f07..2fa0a44 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: polyseed description: A pure dart implementation of the 16-word seed scheme for monero -version: 0.0.6 +version: 0.0.7 homepage: https://cakelabs.com repository: https://github.com/cake-tech/polyseed_dart issue_tracker: https://github.com/cake-tech/polyseed_dart/issues diff --git a/test/polyseed_test.dart b/test/polyseed_test.dart index a9e4142..d1abcf0 100644 --- a/test/polyseed_test.dart +++ b/test/polyseed_test.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:polyseed/polyseed.dart'; import 'package:polyseed/src/mnemonics/es_lang.dart'; +import 'package:polyseed/src/mnemonics/jp_lang.dart'; import 'package:test/test.dart'; void main() { @@ -118,6 +119,24 @@ void main() { expect(legacySeed, "remedio haz ébano lobo orden celda pezuña regreso ardilla estar acelga fallo punto nación hada quitar ancla obeso piedra pausa helio fuente joroba pista quitar"); }); + + test('Edge Case: Japanese seed with normal space separator', () { + final seed = + "せつぶん いせい てはい けんか たてる ねんきん くたびれる いよく やたい あいこくしん ちきゅう きたえる せたけ あひる かのう げつれい"; + + final detectedLang = PolyseedLang.getByPhrase(seed); + expect(detectedLang, jpLang); + + final isValidSeed = Polyseed.isValidSeed(seed); + expect(isValidSeed, true); + + final key = Polyseed.decode(seed, jpLang, coin) + .generateKey(coin, 32) + .toHexString(); + + expect(key, + "340593b3fd0b2670b4727b6a9b64f3f817e2b97fcb26ea3ae8467234eb857f95"); + }); }); }); }