Merge branch 'add_mnemonic_language_detection_utils' into 'development'
Mnemonic Language Detection Utils See merge request GeneralProtocols/xo/stack!103
This commit is contained in:
@@ -1,9 +1,19 @@
|
||||
import {
|
||||
bip39WordListEnglish,
|
||||
decodeBip39MnemonicNonStandard,
|
||||
deriveSeedFromBip39Mnemonic,
|
||||
encodeBip39MnemonicNonStandard,
|
||||
generateBip39MnemonicNonStandard,
|
||||
// Word lists.
|
||||
bip39WordListChineseSimplified,
|
||||
bip39WordListChineseTraditional,
|
||||
bip39WordListCzech,
|
||||
bip39WordListEnglish,
|
||||
bip39WordListFrench,
|
||||
bip39WordListItalian,
|
||||
bip39WordListJapanese,
|
||||
bip39WordListKorean,
|
||||
bip39WordListPortuguese,
|
||||
bip39WordListSpanish,
|
||||
} from '@bitauth/libauth';
|
||||
import { Bytes } from './bytes.js';
|
||||
|
||||
@@ -253,15 +263,79 @@ export class Mnemonic {
|
||||
* Checks if a word exists in the provided dictionary.
|
||||
*
|
||||
* @param word Word to check.
|
||||
* @param dictionary Optional wordlist (defaults to English BIP-39 wordlist).
|
||||
* @param wordList Optional wordlist (defaults to English BIP-39 wordlist).
|
||||
*
|
||||
* @returns boolean indicating if the word is valid.
|
||||
*/
|
||||
public static isValidWord(
|
||||
word: string,
|
||||
dictionary: string[] = bip39WordListEnglish,
|
||||
wordList: string[] = bip39WordListEnglish,
|
||||
): boolean {
|
||||
return dictionary.includes(word.toLowerCase());
|
||||
return wordList.includes(word.toLowerCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect which languages (word-lists) the given mnemonic matches.
|
||||
*
|
||||
* @param words The mnemonic words to check
|
||||
* @param wordLists The BIP39 word lists to check against, keyed by language name
|
||||
* @param allowPartialMatches If true, treats incomplete words as valid matches (useful for autocomplete)
|
||||
*
|
||||
* @returns Object mapping language names to their word lists for all matching languages
|
||||
*/
|
||||
public static detectLanguages(
|
||||
words: Array<string>,
|
||||
wordLists: { [name: string]: Array<string> },
|
||||
allowPartialMatches = false,
|
||||
): { [name: string]: Array<string> } {
|
||||
// Define our match function.
|
||||
const isWordMatch = (word: string, wordList: Array<string>): boolean => {
|
||||
if (allowPartialMatches && word.length === 0) return true;
|
||||
if (wordList.includes(word)) return true;
|
||||
return (
|
||||
allowPartialMatches &&
|
||||
wordList.some((fullWord) => fullWord.startsWith(word))
|
||||
);
|
||||
};
|
||||
|
||||
// Iterate over each word-list and check whether it's a match.
|
||||
return Object.fromEntries(
|
||||
Object.entries(wordLists).filter(([, wordList]) =>
|
||||
words.every((word) => isWordMatch(word, wordList)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all valid BIP39 words from languages that match the given mnemonic words.
|
||||
*
|
||||
* Uses partial matching to determine eligible languages, then returns all words
|
||||
* from those languages (useful for autocomplete and word validation).
|
||||
*
|
||||
* @param words The mnemonic words to check (can be partial/incomplete)
|
||||
* @param wordLists The BIP39 word lists to check against, keyed by language name
|
||||
* @returns Array of all unique words from matching languages, sorted alphabetically
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // Get all valid words for languages that match partial input
|
||||
* const eligibleWords = Mnemonic.getEligibleWords(['aban', 'abil'], wordLists);
|
||||
* // Returns all words from languages where 'aban*' and 'abil*' are valid prefixes
|
||||
*
|
||||
* // Filter for autocomplete
|
||||
* const completions = eligibleWords.filter(word => word.startsWith('acc'));
|
||||
* ```
|
||||
*/
|
||||
public static getEligibleWords(
|
||||
words: Array<string>,
|
||||
wordLists: { [name: string]: Array<string> },
|
||||
): Array<string> {
|
||||
// Given the words provided, find eligible languages/word-lists.
|
||||
const matchingLanguages = this.detectLanguages(words, wordLists, true);
|
||||
|
||||
// Merge all eligible word completions into an Array.
|
||||
// NOTE: We use a Set to de-deuplicate words that might appear in multiple word-lists.
|
||||
return [...new Set<string>(Object.values(matchingLanguages).flat())];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -347,3 +421,24 @@ export class Mnemonic {
|
||||
return this.mnemonic.passphrase;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: This is handled exceptionally due to tree-shaking considerations.
|
||||
// Word lists incur a large cost on the bundle size and LibAuth considers non-English word-lists as non-standard:
|
||||
// https://github.com/bitauth/libauth/blob/master/src/lib/key/bip39.ts#L178
|
||||
// And the BIP39 spec warns against their usage:
|
||||
// https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki#wordlists
|
||||
// If we include this in the class, it cannot be tree-shaken by most tooling, so we define a stand-alone function instead.
|
||||
export function getAllBIP39WordLists() {
|
||||
return {
|
||||
'Chinese Simplified': bip39WordListChineseSimplified,
|
||||
'Chinese Traditional': bip39WordListChineseTraditional,
|
||||
Czech: bip39WordListCzech,
|
||||
English: bip39WordListEnglish,
|
||||
French: bip39WordListFrench,
|
||||
Italian: bip39WordListItalian,
|
||||
Japanese: bip39WordListJapanese,
|
||||
Korean: bip39WordListKorean,
|
||||
Portuguese: bip39WordListPortuguese,
|
||||
Spanish: bip39WordListSpanish,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user